package cn.pm.datasource.sharding.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Create by spf on 2019/03/13
 * redis 工具类
 */
@Component
public class RedisUtil {
    // redis前缀，每个项目都应在配置文件中指定自己的项目的前缀
    private static String prefix;
    @Value("${spring.redis.prefix}")
    private void setPrefix(String prefix) {
        RedisUtil.prefix = prefix + ":";
    }
    public static String getPrefix() {
        return prefix;
    }

    private static RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisUtil.redisTemplate = redisTemplate;
    }

    /** set String */
    public static void set(String key, String value) {
        redisTemplate.opsForValue().set(prefix + key, value);
    }
    // 设置key-value并指定生存时间，单位 秒
    public static void set( String key, String value, int timeOutSeconds) {
        redisTemplate.opsForValue().set(prefix + key, value, timeOutSeconds, TimeUnit.SECONDS);
    }
    /** get String */
    public static String get( String key) {
        return (String) redisTemplate.opsForValue().get(prefix + key);
    }

    /** set Object */
    public static void setObject( String key, Object value) {
        redisTemplate.opsForValue().set(prefix + key, value);
    }
    // 设置key-value并指定生存时间，单位 秒
    public static void setObject( String key, Object value, int timeOutSeconds) {
        redisTemplate.opsForValue().set(prefix + key, value, timeOutSeconds, TimeUnit.SECONDS);
    }
    /** get Object */
    public static Object getObject( String key) {
        return redisTemplate.opsForValue().get(prefix + key);
    }

    /**
     * delete
     */
    public static void delete(String... keys) {
        if (keys != null && keys.length > 0) {
            for (int i = 0; i < keys.length; i++) {
                keys[i] = prefix + keys[i];
            }
            if (keys.length == 1) {
                redisTemplate.delete(keys[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(keys));
            }
        }
    }

    /**
     * 根据key表达式  删除所有匹配上的key
     */
    public static void deleteByPattern( String keyPattern) {
        Set<String> keys = keys(keyPattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 自增，并获取最新的值
     * 首次调用，将返回1
     *
     * 先自增，再返回最新的值，如果需要不自增或先返回再自增等，请参考以下具体实现，手动调用其具体方法
     * @param timeOutSeconds：key有效期，为null 将永久有效
     */
    public static long increment( String key, Long timeOutSeconds) {
        return increment(key, null, timeOutSeconds);
    }
    // initValue：初始化值，如果不为null，调用将返回initValue本身
    public static long increment( String key, Integer initValue, Long timeOutSeconds) {
        RedisAtomicLong entityIdCounter;
        if (initValue == null) {
            entityIdCounter = new RedisAtomicLong(prefix + key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
        } else {
            entityIdCounter = new RedisAtomicLong(prefix + key, Objects.requireNonNull(redisTemplate.getConnectionFactory()), initValue);
        }
        long incrValue = entityIdCounter.incrementAndGet();
        if (timeOutSeconds != null)
            entityIdCounter.expire(timeOutSeconds, TimeUnit.SECONDS);
        return incrValue;
    }
    // 仅用于获取 通过increment自增生成的值（首次调用直接获取值，如果key不存在，将会直接创建key，且失效时间为永久）
    public static long incrementGet( String key) {
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(prefix + key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
        return entityIdCounter.get();
    }


    // region list集合操作

    /**
     * 将一个或多个值 value 插入到列表 key ，从左侧依次插入
     */
    // 附带超时时间 秒
    public static void l_leftPush( String key, Long timeOutSeconds, Object... values) {
        String realKey = prefix + key;
        redisTemplate.opsForList().leftPushAll(realKey, values);
        if (timeOutSeconds != null)
            redisTemplate.expire(realKey, timeOutSeconds, TimeUnit.SECONDS);
    }
    // 从队列的左侧，获取一个元素（不移除）
    public static Object l_leftGet( String key) {
        List<Object> range = redisTemplate.opsForList().range(prefix + key, 0, 0);
        if (range == null || range.isEmpty())
            return null;
        return range.get(0);
    }
    // 从队列的左侧，获取一个元素，并移除
    public static Object l_leftPop( String key) {
        return redisTemplate.opsForList().leftPop(prefix + key);
    }

    /**
     * 将一个或多个值 value 插入到列表 key ，从右侧依次插入
     */
    // 附带超时时间 秒
    public static void l_rightPush( String key, Long timeOutSeconds, Object... values) {
        String realKey = prefix + key;
        redisTemplate.opsForList().rightPushAll(realKey, values);
        if (timeOutSeconds != null)
            redisTemplate.expire(realKey, timeOutSeconds, TimeUnit.SECONDS);
    }
    // 从队列的右侧，获取一个元素（不移除）
    public static Object l_rightGet( String key) {
        List<Object> range = redisTemplate.opsForList().range(prefix + key, -1, -1);
        if (range == null || range.isEmpty())
            return null;
        return range.get(0);
    }
    // 从队列的右侧，获取一个元素，并移除
    public static Object l_rightPop( String key) {
        return redisTemplate.opsForList().rightPop(prefix + key);
    }

    /**
     * 从列表中 获取指定下标的元素 （包含头，包含尾）
     *
     * 下标越界时不会抛出异常，实际返回的list长度可能会小于预期的长度
     * 当下标为正数时：
     *     从左侧开始，第一个下标元素为 0，第二个元素下标为 1，以此类推
     * 当下标为负数时：
     *     从左侧开始，倒数第一个元素下标为 -1，倒数第二个元素下标为 -2，以此类推
     *
     *     例如：
     *       获取全部元素        startIndex = 0      endIndex = -1
     *       获取倒数第一个元素   startIndex = -1      endIndex = -1
     *       获取倒数两个元素    startIndex = -2      endIndex = -1
     *
     * @param startIndex 元素下标，从左侧开始，第一个元素下标为0
     * @param endIndex   元素下标，返回的列表中也将包含此下标指向的元素
     */
    public static List<Object> l_list( String key, int startIndex, int endIndex) {
        return redisTemplate.opsForList().range(prefix + key, startIndex, endIndex);
    }
    // 获取所有元素
    public static List<Object> l_listAll( String key) {
        return l_list(key, 0, -1);
    }

    /**
     * 为指定下标 设置值
     * 当下标越界，或key不存在时，将抛出异常
     */
    public static void l_setByIndex( String key, int index,Object value) {
        redisTemplate.opsForList().set(prefix + key, index, value);
    }

    /**
     * 获取指定下标的元素
     * 当下标越界，不会抛出异常
     */
    public static Object l_getByIndex( String key, int index) {
        return redisTemplate.opsForList().index(prefix + key, index);
    }

    /**
     * 获取列表的长度
     */
    public static long l_size( String key) {
        Long size = redisTemplate.opsForList().size(prefix + key);
        if (size == null)
            return 0;
        return size;
    }
    // endregion

    // region set集合操作

    /**
     * 将一个元素添加到set集合中
     * 如果key不存在，将会创建
     *
     * @param timeOutSeconds 超时时间，如果为null 将永不过期
     */
    public static void s_add( String key, Long timeOutSeconds, Object... values) {
        String realKey = prefix + key;
        redisTemplate.opsForSet().add(realKey, values);
        if (timeOutSeconds != null)
            redisTemplate.expire(realKey, timeOutSeconds, TimeUnit.SECONDS);
    }

    /**
     * 获取siz长度
     */
    public static long s_size( String key) {
        Long size = redisTemplate.opsForSet().size(prefix + key);
        if (size == null)
            return 0;
        return size;
    }

    /**
     * 检查 本集合中是否包含指定value
     */
    public static boolean s_hasValue( String key, Object value) {
        Boolean hasValue = redisTemplate.opsForSet().isMember(prefix + key, value);
        if (hasValue == null)
            return false;
        return hasValue;
    }

    /**
     * 返回本集合的所有元素
     */
    public static Set<Object> s_getAll( String key) {
        return redisTemplate.opsForSet().members(prefix + key);
    }

    /**
     * 从set集合中 移除元素
     */
    public static void s_remove( String key, Object... values) {
        redisTemplate.opsForSet().remove(prefix + key, values);
    }

    /**
     * 将指定元素 从本集合移动到目标集合
     */
    public static void s_move( String key, Object value,  String destKey) {
        redisTemplate.opsForSet().move(prefix + key, value, prefix + destKey);
    }

    /**
     * 随机从集合中移除一个元素 并返回
     */
    public static Object s_pop( String key) {
        return redisTemplate.opsForSet().pop(prefix + key);
    }
    // 指定移除几个元素
    public static List<Object> s_pop( String key, long count) {
        return redisTemplate.opsForSet().pop(prefix + key, count);
    }

    /**
     * 随机从集合中返回一个元素 （不移除）
     */
    public static Object s_randGet( String key) {
        return redisTemplate.opsForSet().randomMember(prefix + key);
    }
    // 指定返回几个元素 (返回的结果中的元素可能会重复)
    public static List<Object> s_randGet( String key, long count) {
        return redisTemplate.opsForSet().randomMembers(prefix + key, count);
    }
    // 指定返回几个元素 (返回的结果中的元素不会重复)
    public static Set<Object> s_randGetDistinct( String key, long count) {
        return redisTemplate.opsForSet().distinctRandomMembers(prefix + key, count);
    }

    /**
     * 返回 本集合 和 otherKey代表的集合 之间的差集
     * (即本集合中有，而其他集合中没有的元素)
     */
    public static Set<Object> s_diff( String key, String... otherKeys) {
        if (otherKeys.length == 1) {
            return redisTemplate.opsForSet().difference(prefix + key, prefix + otherKeys[0]);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            return redisTemplate.opsForSet().difference(prefix + key, collect);
        }
    }

    /**
     * 计算出 本集合 和 otherKey代表的集合 之间的差集(即本集合中有，而其他集合中没有的元素)
     * 并将结果存储到destKey中，如果destKey已有数据，将覆盖
     */
    public static void s_diffAndStore( String key,  String destKey, String... otherKeys) {
        if (otherKeys.length == 1) {
            redisTemplate.opsForSet().differenceAndStore(prefix + key, prefix + otherKeys[0], prefix + destKey);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            redisTemplate.opsForSet().differenceAndStore(prefix + key, collect, prefix + destKey);
        }
    }

    /**
     * 返回 本集合 和 otherKey代表的集合 之间的交集
     *
     * 如果有某一集合不存在，将会被当做空集合处理，也就是说最终的返回结果也将是一个空集合
     */
    public static Set<Object> s_intersect( String key, String... otherKeys) {
        if (otherKeys.length == 1) {
            return redisTemplate.opsForSet().intersect(prefix + key, prefix + otherKeys[0]);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            return redisTemplate.opsForSet().intersect(prefix + key, collect);
        }
    }

    /**
     * 计算出 本集合 和 otherKey代表的集合 之间的交集
     * 并将结果存储到destKey中，如果destKey已有数据，将覆盖
     *
     * 如果有某一集合不存在，将会被当做空集合处理，也就是说最终的返回结果也将是一个空集合
     */
    public static void s_intersectAndStore( String key,  String destKey, String... otherKeys) {
        if (otherKeys.length == 1) {
            redisTemplate.opsForSet().intersectAndStore(prefix + key, prefix + otherKeys[0], prefix + destKey);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            redisTemplate.opsForSet().intersectAndStore(prefix + key, collect, prefix + destKey);
        }
    }

    /**
     * 返回 本集合 和 otherKey代表的集合 之间的并集
     */
    public static Set<Object> s_union( String key, String... otherKeys) {
        if (otherKeys.length == 1) {
            return redisTemplate.opsForSet().union(prefix + key, prefix + otherKeys[0]);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            return redisTemplate.opsForSet().union(prefix + key, collect);
        }
    }

    /**
     * 计算出 本集合 和 otherKey代表的集合 之间的并集
     * 并将结果存储到destKey中，如果destKey已有数据，将覆盖
     */
    public static void s_unionAndStore( String key,  String destKey, String... otherKeys) {
        if (otherKeys.length == 1) {
            redisTemplate.opsForSet().unionAndStore(prefix + key, prefix + otherKeys[0], prefix + destKey);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            redisTemplate.opsForSet().unionAndStore(prefix + key, collect, prefix + destKey);
        }
    }
    // endregion

    // region zset有序集合操作

    /**
     * 向有序集合中添加一个元素，并指定排序值
     *
     * @param score 排序值
     * @param timeOutSeconds 超时时间，如果为null 则永不超时
     */
    public static void zs_add( String key, Object value, double score, Long timeOutSeconds) {
        redisTemplate.opsForZSet().add(prefix + key, value, score);
        if (timeOutSeconds != null)
            expire(key, timeOutSeconds);
    }

    /**
     * 为指定元素的排序值，增加step
     * 如果key不存在，则创建key并设置value
     * 如果value在key中不包含，则新增value
     */
    public static void zs_incrementScore( String key, Object value, double step) {
        redisTemplate.opsForZSet().incrementScore(prefix + key, value, step);
    }

    public static void zs_incrementScore( String key, Object value) {
        zs_incrementScore(key, value, 1);
    }

    /**
     * 获取指定元素的排序值
     * 如果元素不存在将返回null
     */
    public static Double zs_getScore( String key, Object value) {
        return redisTemplate.opsForZSet().score(prefix + key, value);
    }

    /**
     * 从小到大排序 获取元素排名
     * 排序值最小的，排名为0
     */
    public static Long zs_getIndex( String key, Object value) {
        return redisTemplate.opsForZSet().rank(prefix + key, value);
    }

    /**
     * 从大到小排序 获取元素的排名
     * 也就是说，排序值最大的，排名为0
     */
    public static Long zs_getIndexReverse( String key, Object value) {
        return redisTemplate.opsForZSet().reverseRank(prefix + key, value);
    }

    /**
     * 获取此有序集合的长度
     */
    public static long zs_size( String key) {
        Long size = redisTemplate.opsForZSet().size(prefix + key);
        if (size == null)
            return 0;
        return size;
    }

    /**
     * 移除有序集合中的元素
     */
    public static void zs_remove( String key, Object... values) {
        redisTemplate.opsForZSet().remove(prefix + key, values);
    }

    // 按照从小到大排序，移除下标在 startIndex 到 endIndex之间的元素 （包含头，包含尾）
    public static void zs_remove( String key, long startIndex, long endIndex) {
        redisTemplate.opsForZSet().removeRange(prefix + key, startIndex, endIndex);
    }

    // 按照从小到大排序，移除排序值在 minScore 到 maxScore之间的元素 （包含头，包含尾）
    public static void zs_removeByScore( String key, double minScore, double maxScore) {
        redisTemplate.opsForZSet().removeRangeByScore(prefix + key, minScore, maxScore);
    }

    /**
     * 查询 排序值在 min 和 max 之间的元素数量 （包含头，包含尾）
     */
    public static long zs_count( String key, double minScore, double maxScore) {
        Long size = redisTemplate.opsForZSet().count(prefix + key, minScore, maxScore);
        if (size == null)
            return 0;
        return size;
    }

    /**
     * 从小到大排序，获取下标在startIndex 和 endIndex之间的元素 （包含头，包含尾）
     */
    public static Set<Object> zs_list( String key, long startIndex, long endIndex) {
        return redisTemplate.opsForZSet().range(prefix + key, startIndex, endIndex);
    }
    public static Set<ZSetOperations.TypedTuple<Object>> zs_listWithScores( String key, long startIndex, long endIndex) {
        return redisTemplate.opsForZSet().rangeWithScores(prefix + key, startIndex, endIndex);
    }
    public static Set<Object> zs_listAll( String key) {
        return zs_list(key, 0, -1);
    }
    public static Set<ZSetOperations.TypedTuple<Object>> zs_listAllWithScores( String key) {
        return zs_listWithScores(key, 0, -1);
    }

    /**
     * 从大到小排序，获取下标在startIndex 和 endIndex之间的元素 （包含头，包含尾）
     */
    public static Set<Object> zs_listReverse( String key, long startIndex, long endIndex) {
        return redisTemplate.opsForZSet().reverseRange(prefix + key, startIndex, endIndex);
    }
    public static Set<ZSetOperations.TypedTuple<Object>> zs_listReverseWithScores( String key, long startIndex, long endIndex) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(prefix + key, startIndex, endIndex);
    }
    public static Set<Object> zs_listAllReverse( String key) {
        return zs_listReverse(key, 0, -1);
    }
    public static Set<ZSetOperations.TypedTuple<Object>> zs_listAllReverseWithScores( String key) {
        return zs_listReverseWithScores(prefix + key, 0, -1);
    }

    /**
     * 获取 排序值在 min 和 max 之间的元素数量 （包含头，包含尾）
     */
    public static Set<Object> zs_listByScore( String key, double minScore, double maxScore) {
        return redisTemplate.opsForZSet().rangeByScore(prefix + key, minScore, maxScore);
    }
    // 返回的元素中，包含排序值
    public static Set<ZSetOperations.TypedTuple<Object>> zs_listByScoreWithScores( String key, double minScore, double maxScore) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(prefix + key, minScore, maxScore);
    }

    /**
     * 获取 排序值在 min 和 max 之间的元素数量 （包含头，包含尾）
     * 从小到大排序，按照 offset和count分页获取
     */
    public static Set<Object> zs_listByScoreLimit( String key, double minScore, double maxScore, long offset, long count) {
        return redisTemplate.opsForZSet().rangeByScore(prefix + key, minScore, maxScore, offset, count);
    }
    // 返回的元素中，包含排序值
    public static Set<ZSetOperations.TypedTuple<Object>> zs_listByScoreLimitWithScores( String key, double minScore, double maxScore, long offset, long count) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(prefix + key, minScore, maxScore, offset, count);
    }

    /**
     * 获取 排序值在 min 和 max 之间的元素数量 （包含头，包含尾）
     * 从大到小排序，按照 offset和count分页获取
     */
    public static Set<Object> zs_listReverseByScoreLimit( String key, double minScore, double maxScore, long offset, long count) {
        return redisTemplate.opsForZSet().reverseRangeByScore(prefix + key, minScore, maxScore, offset, count);
    }
    // 返回的元素中，包含排序值
    public static Set<ZSetOperations.TypedTuple<Object>> zs_listReverseByScoreLimitWithScores( String key, double minScore, double maxScore, long offset, long count) {
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(prefix + key, minScore, maxScore, offset, count);
    }


    /**
     * 计算出本集合 与 otherKeys代表的集合 之间的并集  并存储到 destKey集合中
     * 如果destKey已存在或已包含值，将被覆盖
     *
     * 默认将多个集合中的value相同的 score排序值相加，最终作为destKey集合中此元素的排序值
     * 如果有其他需求，如保留 最小、最大等，请参考api自行调用
     * 详情参考文档：http://doc.redisfans.com/sorted_set/zunionstore.html
     */
    public static void zs_unionAndStore( String key,  String destKey, String... otherKeys) {
        if (otherKeys.length == 1) {
            redisTemplate.opsForZSet().unionAndStore(prefix + key, prefix + otherKeys[0], prefix + destKey);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            redisTemplate.opsForZSet().unionAndStore(prefix + key, collect, prefix + destKey);
        }
    }

    /**
     * 计算出本集合 与 otherKeys代表的集合 之间的交集  并存储到 destKey集合中
     * 如果destKey已存在或已包含值，将被覆盖
     *
     * 默认将多个集合中的value相同的 score排序值相加，最终作为destKey集合中此元素的排序值
     * 如果有其他需求，如保留 最小、最大等，请参考api自行调用
     * 详情参考文档：http://doc.redisfans.com/sorted_set/zinterstore.html
     */
    public static void zs_intersectAndStore( String key,  String destKey, String... otherKeys) {
        if (otherKeys.length == 1) {
            redisTemplate.opsForZSet().intersectAndStore(prefix + key, prefix + otherKeys[0], prefix + destKey);
        } else {
            Set<String> collect = Arrays.stream(otherKeys).map(otherKey -> prefix + otherKey).collect(Collectors.toSet());
            redisTemplate.opsForZSet().intersectAndStore(prefix + key, collect, prefix + destKey);
        }
    }
    // endregion

    // region Hash集合操作

    /**
     * 向一个hash集合中 设置键值对，如果不存在将会被创建
     *
     * @param timeOutSeconds 超时时间，如果为null，将永不超时
     */
    public static void h_put( String key, Map<String, Object> map, Long timeOutSeconds) {
        redisTemplate.opsForHash().putAll(prefix + key, map);
        if (timeOutSeconds != null)
            expire(key, timeOutSeconds);
    }
    public static void h_put( String key,  String field, Object value, Long timeOutSeconds) {
        redisTemplate.opsForHash().put(prefix + key, field, value);
        if (timeOutSeconds != null)
            expire(key, timeOutSeconds);
    }
    // 只有当field不存在时，才会设置值
    public static boolean h_putIfAbsent( String key,  String field, Object value, Long timeOutSeconds) {
        Boolean isPut = redisTemplate.opsForHash().putIfAbsent(prefix + key, field, value);
        if (timeOutSeconds != null)
            expire(key, timeOutSeconds);
        return isPut;
    }

    /**
     * 删除hash中指定的 field
     */
    public static void h_removeField( String key, String... fields) {
        redisTemplate.opsForHash().delete(prefix + key, fields);
    }

    /**
     * 判断指定hash表中  是否包含指定field
     */
    public static boolean h_hasField( String key,  String field) {
        return redisTemplate.opsForHash().hasKey(prefix + key, field);
    }

    /**
     * 获取hash表中指定字段的值
     */
    public static Object h_get( String key,  String field) {
        return redisTemplate.opsForHash().get(prefix + key, field);
    }
    public static Map h_getAll( String key) {
        return redisTemplate.opsForHash().entries(prefix + key);
    }
    public static List<Object> h_GetByFields( String key, String... fields) {
        return redisTemplate.opsForHash().multiGet(prefix + key, Arrays.asList(fields));
    }
    public static Set h_keys( String key) {
        return redisTemplate.opsForHash().keys(prefix + key);
    }
    public static List<Object> h_values( String key) {
        return redisTemplate.opsForHash().values(prefix + key);
    }

    /**
     * 为hash表中的指定字段 执行递增，返回递增后的值
     *
     * 当key或field不存在时，将会被创建
     * 当field指定value类型不为数字时，将会报错
     *
     * 注意：
     *      在本项目中，因为value的序列化是以Jdk的序列化来实现的，在redis底层存储中，存储的是java序列字节码而不是数字
     *      也就是说，这里的自增，只能用来自增不存在的字段，如果是手动创建的字段，即便类型是数字，自增时也会报错
     */
    // public static long h_increment( String key,  String field) {
    //     return h_increment(key, field, 1);
    // }
    // public static long h_increment( String key,  String field, long step) {
    //     return redisTemplate.opsForHash().increment(prefix + key, field, step);
    // }
    // endregion

    /**
     * other
     */
    // 获取所有匹配此字符串的key
    public static Set<String> keys( String pattern) {
        // 2020-04-16 调整keys的获取方式
        return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> binaryKeys = new HashSet<>();
            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(prefix + pattern).count(10_000).build())) {
                while (cursor.hasNext()) {
                    binaryKeys.add(new String(cursor.next()));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return binaryKeys;
        });
    }

    // 判断指定的key是否存在
    public static boolean exists( String key) {
        Boolean hasKey = redisTemplate.hasKey(prefix + key);
        return hasKey == null ? false : hasKey;
    }

    /**
     * 指定缓存失效时间 秒
     */
    public static void expire( String key, long seconds) {
        redisTemplate.expire(prefix + key, seconds, TimeUnit.SECONDS);
    }
}
